////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2009 Daniel F. Hanson
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to
// deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
////////////////////////////////////////////////////////////////////////////////

using System;
using System.Collections.Generic;
#if DEBUG
using System.Diagnostics;
#endif
using System.Globalization;

namespace SilverLua.Compiler
{
   internal class Parser
   {
      internal SymbolTable SymbolTable { get; private set; }
      internal List<ChunkScope> ChunkList { get { return SymbolTable.Chunks; } }
      
      private Lexer _lexer;
      
      private Token _peek;
      private Token _doublePeek;
      
      private static readonly Dictionary<string, int> OperatorPrecedence = new Dictionary<string, int>();
      private const int UnaryOperatorPrecedence = 7;

      private int GetOperatorPrecedence(Token token)
      {
         if (token.Type == TokenType.UnaryOperator)
         {
            return UnaryOperatorPrecedence;
         }
         else
         {
            int precedence;
            if (OperatorPrecedence.TryGetValue(token.String, out precedence))
            {
               return precedence;
            }
            else
            {
               return 0;
            }
         }
      }

      static Parser()
      {
         OperatorPrecedence.Add("or",  1);
         OperatorPrecedence.Add("and", 2);
         OperatorPrecedence.Add("<",   3);
         OperatorPrecedence.Add(">",   3);
         OperatorPrecedence.Add("<=",  3);
         OperatorPrecedence.Add(">=",  3);
         OperatorPrecedence.Add("~=",  3);
         OperatorPrecedence.Add("==",  3);
         OperatorPrecedence.Add("..",  4);
         OperatorPrecedence.Add("+",   5);
         OperatorPrecedence.Add("-",   5);
         OperatorPrecedence.Add("*",   6);
         OperatorPrecedence.Add("/",   6);
         OperatorPrecedence.Add("%",   6);
         OperatorPrecedence.Add("^",   8);
      }
      
      internal Parser(Lexer lexer)
      {
         SymbolTable = new SymbolTable();
         _lexer = lexer;
      }
      
      internal StatementNode[] Parse()
      {
         _peek = _lexer.IsEOF ? Token.EOF : _lexer.NextToken(false);
         _doublePeek = _peek == Token.EOF ? Token.EOF : _lexer.NextToken(false);
         
         List<StatementNode> script = new List<StatementNode>(ParseBlock());
         script.Add(new ReturnStatementNode(new ExpressionNode[0]));
         
         return script.ToArray();
      }
      
      private void GetNextToken()
      {
         if (_peek == Token.EOF) throw new Exception("EOF reached");
         
         _peek = _doublePeek;
         _doublePeek = _peek == Token.EOF ? Token.EOF : _lexer.NextToken(false);
      }

      private StatementNode[] ParseBlock()
      {
         SymbolTable.PushBlock();
         StatementNode[] block = ParseBlockNoPush(false);
         SymbolTable.PopBlock();
         
         return block;
      }
      
      private StatementNode[] ParseBlockNoPush(bool addReturn)
      {
         List<StatementNode> block = new List<StatementNode>();

         while (IsStatementBegin(_peek))
         {
            block.Add(ParseStatement());
         }

         if (IsTokenMatch(_peek, TokenType.Keyword, "return")) block.Add(ParseReturn());
         else if (IsTokenMatch(_peek, TokenType.Keyword, "break")) block.Add(ParseBreak());
         
         if (addReturn) block.Add(new ReturnStatementNode(new ExpressionNode[0]));

         return block.ToArray();
      }

      private StatementNode[] ParseDoEndBlock()
      {
         ForceToken(TokenType.Keyword, "do");
         StatementNode[] block = ParseBlock();
         ForceToken(TokenType.Keyword, "end");
         
         return block;
      }
      
      private WhileStatementNode ParseWhile()
      {
         ForceToken(TokenType.Keyword, "while");
         ExpressionNode condition = ParseExpression();
         StatementNode[] block = ParseDoEndBlock();
         
         return new WhileStatementNode(condition, block);
      }
      
      private RepeatStatementNode ParseRepeat()
      {
         ForceToken(TokenType.Keyword, "repeat");
         StatementNode[] block = ParseBlock();
         ForceToken(TokenType.Keyword, "until");
         ExpressionNode condition = ParseExpression();
         
         return new RepeatStatementNode(block, condition);
      }
      
      private IfStatementNode ParseIf()
      {
         List<ExpressionNode> conditions = new List<ExpressionNode>();
         List<StatementNode[]> blocks = new List<StatementNode[]>();
         
         ForceToken(TokenType.Keyword, "if");
         conditions.Add(ParseExpression());
         ForceToken(TokenType.Keyword, "then");
         blocks.Add(ParseBlock());
         
         while (IsTokenMatch(_peek, TokenType.Keyword, "elseif"))
         {
            GetNextToken();
            conditions.Add(ParseExpression());
            ForceToken(TokenType.Keyword, "then");
            blocks.Add(ParseBlock());
         }
         
         if (IsTokenMatch(_peek, TokenType.Keyword, "else"))
         {
            GetNextToken();
            blocks.Add(ParseBlock());
         }
         
         ForceToken(TokenType.Keyword, "end");
         
         return new IfStatementNode(conditions.ToArray(), blocks.ToArray());
      }
      
      private StatementNode ParseFor()
      {
         ForceToken(TokenType.Keyword, "for");
         
         if (IsTokenMatch(_doublePeek, TokenType.Punctuation, "="))
         {
            // Numeric for
            string index = ParseIdentifier();
            GetNextToken();
            ExpressionNode start = ParseExpression();
            ForceToken(TokenType.Punctuation, ",");
            ExpressionNode end = ParseExpression();
            ExpressionNode step = null;
            if (IsTokenMatch(_peek, TokenType.Punctuation, ","))
            {
               GetNextToken();
               step = ParseExpression();
            }
            else
            {
               step = new IntegerLiteralNode(1);
            }
            ForceToken(TokenType.Keyword, "do");
            SymbolTable.PushBlock();
            Symbol indexSymbol = SymbolTable.CurrentChunk.AddLocal(index);
            StatementNode[] block = ParseBlockNoPush(false);
            SymbolTable.PopBlock();
            ForceToken(TokenType.Keyword, "end");
            
            return new NumericForStatementNode(indexSymbol, start, end, step, block);
         }
         else
         {
            // Generic for
            string[] indices = ParseIdentifierList();
            ForceToken(TokenType.Keyword, "in");
            ExpressionNode[] exprList = ParseExpressionList();
            ForceToken(TokenType.Keyword, "do");
            SymbolTable.PushBlock();
            Symbol[] indexSymbols = new Symbol[indices.Length];
            for (int i = 0; i < indices.Length; ++i)
            {
               indexSymbols[i] = SymbolTable.CurrentChunk.AddLocal(indices[i]);
            }
            StatementNode[] block = ParseBlockNoPush(false);
            SymbolTable.PopBlock();
            ForceToken(TokenType.Keyword, "end");
            
            return new GenericForStatementNode(indexSymbols, exprList, block);
         }
      }
      
      private FunctionStatementNode ParseFunction()
      {
         List<string> nameList = new List<string>();
         bool isMethod = false;
         
         ForceToken(TokenType.Keyword, "function");
         
         string firstString = ParseIdentifier();
         
         Symbol firstName = SymbolTable.CurrentChunk.ResolveName(firstString, ChunkList);

         while (IsTokenMatch(_peek, TokenType.Punctuation, "."))
         {
            GetNextToken();
            nameList.Add(ParseIdentifier());
         }

         if (IsTokenMatch(_peek, TokenType.Punctuation, ":"))
         {
            GetNextToken();
            nameList.Add(ParseIdentifier());
            isMethod = true;
         }
         
         string[] paramList = ParseParameterList();
         int chunkIndex = SymbolTable.PushChunk();
         int startParam = 0;
         if (isMethod)
         {
            SymbolTable.CurrentChunk.AddParameter("self", 0);
            startParam = 1;
         }
         for (int i = startParam; i < paramList.Length + startParam; ++i)
         {
            SymbolTable.CurrentChunk.AddParameter(paramList[i], i);
         }
         StatementNode[] block = ParseBlockNoPush(true);
         SymbolTable.PopChunk();
         ForceToken(TokenType.Keyword, "end");

         return new FunctionStatementNode(firstName, nameList.ToArray(), paramList.Length, block, chunkIndex);
      }

      private StatementNode ParseLocal()
      {
         ForceToken(TokenType.Keyword, "local");
         
         if (IsTokenMatch(_peek, TokenType.Keyword, "function"))
         {
            return ParseLocalFunction();
         }
         else
         {
            return ParseLocalVar();
         }
      }
      
      private LocalFunctionStatementNode ParseLocalFunction()
      {
         ForceToken(TokenType.Keyword, "function");
         string name = ParseIdentifier();
         Symbol functionSymbol = SymbolTable.CurrentChunk.AddLocal(name);
         string[] paramList = ParseParameterList();
         int chunkIndex = SymbolTable.PushChunk();
         for (int i = 0; i < paramList.Length; ++i)
         {
            SymbolTable.CurrentChunk.AddParameter(paramList[i], i);
         }
         StatementNode[] block = ParseBlockNoPush(true);
         SymbolTable.PopChunk();
         ForceToken(TokenType.Keyword, "end");

         return new LocalFunctionStatementNode(functionSymbol, paramList.Length, block, chunkIndex);
      }
      
      private AssignStatementNode ParseLocalVar()
      {
         string[] varNames = ParseIdentifierList();
         ExpressionNode[] exprList;
         if (IsTokenMatch(_peek, TokenType.Punctuation, "="))
         {
            GetNextToken();
            exprList = ParseExpressionList();
         }
         else
         {
            exprList = new ExpressionNode[0];
         }
         
         VariableNode[] varList = new VariableNode[varNames.Length];
         for (int i = 0; i < varNames.Length; ++i)
         {
            varList[i] = new NamedVariableNode(SymbolTable.CurrentChunk.AddLocal(varNames[i]));
         }
         
         return new AssignStatementNode(varList, exprList);
      }

      private ReturnStatementNode ParseReturn()
      {
         ForceToken(TokenType.Keyword, "return");
         ExpressionNode[] retList = null;
         if (IsStatementBegin(_peek)) retList = ParseExpressionList();
         
         return new ReturnStatementNode(retList);
      }

      private BreakStatementNode ParseBreak()
      {
         ForceToken(TokenType.Keyword, "break");
         
         return new BreakStatementNode();
      }
      
      private StatementNode ParseAssignOrFunctionCall()
      {
         PrefixExpressionNode prefix = ParsePrefixExpression();
         
         if (prefix.IsVariableNode)
         {
            #if DEBUG
            Debug.Assert(prefix is VariableNode);
            #endif
            
            // Assign statement
            List<VariableNode> varList = new List<VariableNode>();
            varList.Add((VariableNode)prefix);
            
            while (IsTokenMatch(_peek, TokenType.Punctuation, ","))
            {
               GetNextToken();
               PrefixExpressionNode varNode = ParsePrefixExpression();
               #if DEBUG
               Debug.Assert(varNode is VariableNode);
               #endif
               varList.Add((VariableNode)varNode);
            }
            
            ForceToken(TokenType.Punctuation, "=");
            
            ExpressionNode[] exprList = ParseExpressionList();
            
            return new AssignStatementNode(varList.ToArray(), exprList);
         }
         else
         {
            // Function call
            /*string method = null;

            if (IsTokenMatch(_peek, TokenType.Punctuation, ":"))
            {
               GetNextToken();
               method = ParseIdentifier();
            }

            ExpressionNode[] args = ParseArgumentList();
            
            return new FunctionCallStatementNode(method != null ? new FunctionCallNode(prefix, method, args)
                                                                : new FunctionCallNode(prefix, args));*/
            
            #if DEBUG
            Debug.Assert(prefix is FunctionCallNode);
            #endif
            
            return new FunctionCallStatementNode((FunctionCallNode)prefix);
         }
      }
      
      private PrefixExpressionNode ParsePrefixExpression()
      {
         PrefixExpressionNode prefix;
         
         if (IsTokenMatch(_peek, TokenType.Punctuation, "("))
         {
            GetNextToken();
            ExpressionNode expr = ParseExpression();
            ForceToken(TokenType.Punctuation, ")");
            
            prefix = new ExpressionPrefixExpressionNode(expr);
         }
         else
         {
            string name = _peek.String;
            ForceTokenType(TokenType.Identifier);
            
            prefix = new NamedVariableNode(SymbolTable.CurrentChunk.ResolveName(name, ChunkList));
         }
         
         while (true)
         {
            if (IsTokenMatch(_peek, TokenType.Punctuation, "["))
            {
               GetNextToken();
               ExpressionNode expr = ParseExpression();
               ForceToken(TokenType.Punctuation, "]");
               
               prefix = new SubscriptVariableNode(prefix, expr);
            }
            else if (IsTokenMatch(_peek, TokenType.Punctuation, "."))
            {
               GetNextToken();
               string name = ParseIdentifier();
               
               prefix = new MemberVariableNode(prefix, name);
            }
            else if (IsTokenMatch(_peek, TokenType.Punctuation, "(") ||
                     IsTokenMatch(_peek, TokenType.Punctuation, "{") ||
                     _peek.Type == TokenType.StringLiteral ||
                     _peek.Type == TokenType.LongStringLiteral)
            {
               ExpressionNode[] exprList = ParseArgumentList();
               
               prefix = new FunctionCallNode(prefix, exprList);
            }
            else if (IsTokenMatch(_peek, TokenType.Punctuation, ":"))
            {
               GetNextToken();
               string name = ParseIdentifier();
               ExpressionNode[] exprList = ParseArgumentList();
               
               prefix = new FunctionCallNode(prefix, name, exprList);
            }
            else
            {
               break;
            }
         }
         
         return prefix;
      }
      
      private ExpressionNode ParseExpression()
      {
         Stack<ExpressionNode> operandStack = new Stack<ExpressionNode>();
         Stack<Token> operatorStack = new Stack<Token>();
         int nextPrecedence = 1;
         
         while (nextPrecedence > 0)
         {
            while (_peek.Type == TokenType.UnaryOperator)
            {
               operatorStack.Push(_peek);
               GetNextToken();
            }
            
            if (IsTokenMatch(_peek, TokenType.Keyword, "nil"))
            {
               operandStack.Push(new NilLiteralNode());
               GetNextToken();
            }
            else if (IsTokenMatch(_peek, TokenType.Keyword, "false"))
            {
               operandStack.Push(new BooleanLiteralNode(false));
               GetNextToken();
            }
            else if (IsTokenMatch(_peek, TokenType.Keyword, "true"))
            {
               operandStack.Push(new BooleanLiteralNode(true));
               GetNextToken();
            }
            else if (_peek.Type == TokenType.NumericLiteral)
            {
               operandStack.Push(new IntegerLiteralNode(Convert.ToInt32(_peek.String)));
               GetNextToken();
            }
            else if (_peek.Type == TokenType.HexNumericLiteral)
            {
               operandStack.Push(new IntegerLiteralNode(Convert.ToInt32(_peek.String.Substring(2), 16)));
               GetNextToken();
            }
            else if (_peek.Type == TokenType.ScientificNumericLiteral)
            {
               operandStack.Push(new FloatLiteralNode(Convert.ToSingle(_peek.String, CultureInfo.InvariantCulture)));
               GetNextToken();
            }
            else if (_peek.Type == TokenType.StringLiteral ||
                     _peek.Type == TokenType.LongStringLiteral)
            {
               operandStack.Push(new StringLiteralNode(_peek.String, SymbolTable.CurrentChunk.ResolveString(_peek.String)));
               GetNextToken();
            }
            else if (IsTokenMatch(_peek, TokenType.Keyword, "function"))
            {
               GetNextToken();
               string[] parameters = ParseParameterList();
               int chunkIndex = SymbolTable.PushChunk();
               for (int i = 0; i < parameters.Length; ++i)
               {
                  SymbolTable.CurrentChunk.AddParameter(parameters[i], i);
               }
               StatementNode[] block = ParseBlockNoPush(true);
               SymbolTable.PopChunk();
               ForceToken(TokenType.Keyword, "end");
               operandStack.Push(new FunctionExpressionNode(parameters.Length, block, chunkIndex));
            }
            else if (IsTokenMatch(_peek, TokenType.Punctuation, "{"))
            {
               GetNextToken();
               operandStack.Push(ParseFieldList());
               ForceToken(TokenType.Punctuation, "}");
            }
            else
            {
               operandStack.Push(ParsePrefixExpression());
            }

            nextPrecedence = GetOperatorPrecedence(_peek);

            while (operatorStack.Count > 0 &&
                   nextPrecedence <= GetOperatorPrecedence(operatorStack.Peek()))
            {
               // Check if operator is right-associative; if so, don't pop yet.
               if (_peek.String == operatorStack.Peek().String &&
                   (_peek.String == ".." || _peek.String == "^"))
               {
                  break;
               }
               
               Token op = operatorStack.Pop();

               if (op.Type == TokenType.UnaryOperator)
               {
                  ExpressionNode operand = operandStack.Pop();
                  operandStack.Push(new UnaryExpressionNode(op.String, operand));
               }
               else
               {
                  #if DEBUG
                  Debug.Assert(op.Type == TokenType.BinaryOperator);
                  #endif
                  
                  ExpressionNode rightOperand = operandStack.Pop();
                  ExpressionNode leftOperand = operandStack.Pop();
                  if (op.String == ">" || op.String == ">=")
                  {
                     operandStack.Push(new BinaryExpressionNode(op.String.Replace('>', '<'), rightOperand, leftOperand));
                  }
                  else if (op.String == "and")
                  {
                     operandStack.Push(new ShortCircuitExpressionNode(leftOperand, rightOperand, false));
                  }
                  else if (op.String == "or")
                  {
                     operandStack.Push(new ShortCircuitExpressionNode(leftOperand, rightOperand, true));
                  }
                  else
                  {
                     operandStack.Push(new BinaryExpressionNode(op.String, leftOperand, rightOperand));
                  }
               }
            }
            
            if (nextPrecedence > 0)
            {
               operatorStack.Push(new Token(_peek.String, TokenType.BinaryOperator));
               GetNextToken();
            }
         }
         
         #if DEBUG
         Debug.Assert(operandStack.Count == 1);
         Debug.Assert(operatorStack.Count == 0);
         #endif
         
         return operandStack.Peek();
      }
      
      private ExpressionNode[] ParseExpressionList()
      {
         List<ExpressionNode> exprList = new List<ExpressionNode>();
         
         exprList.Add(ParseExpression());

         while (IsTokenMatch(_peek, TokenType.Punctuation, ","))
         {
            GetNextToken();
            exprList.Add(ParseExpression());
         }
         
         return exprList.ToArray();
      }
      
      private ExpressionNode[] ParseArgumentList()
      {
         ExpressionNode[] exprList = null;
         
         if (IsTokenMatch(_peek, TokenType.Punctuation, "("))
         {
            GetNextToken();
            
            if (!IsTokenMatch(_peek, TokenType.Punctuation, ")"))
            {
               exprList = ParseExpressionList();
            }
            
            ForceToken(TokenType.Punctuation, ")");
         }
         else if (IsTokenMatch(_peek, TokenType.Punctuation, "{"))
         {
            GetNextToken();
            TableExpressionNode table = ParseFieldList();
            ForceToken(TokenType.Punctuation, "}");
            
            exprList = new ExpressionNode[] { table };
         }
         else if (_peek.Type == TokenType.StringLiteral ||
                  _peek.Type == TokenType.LongStringLiteral)
         {
            exprList = new ExpressionNode[] { new StringLiteralNode(_peek.String, SymbolTable.CurrentChunk.ResolveString(_peek.String)) };
            
            GetNextToken();
         }

         return exprList ?? new ExpressionNode[0];
      }
      
      private void ParseField(ref int index, out ExpressionNode key, out ExpressionNode value)
      {
         if (IsTokenMatch(_peek, TokenType.Punctuation, "["))
         {
            GetNextToken();
            key = ParseExpression();
            ForceToken(TokenType.Punctuation, "]");
            ForceToken(TokenType.Punctuation, "=");
            value = ParseExpression();
         }
         else if (_peek.Type == TokenType.Identifier &&
                  IsTokenMatch(_doublePeek, TokenType.Punctuation, "="))
         {
            string name = ParseIdentifier();
            key = new StringLiteralNode(name, SymbolTable.CurrentChunk.ResolveString(name));
            GetNextToken();
            value = ParseExpression();
         }
         else
         {
            key = new IntegerLiteralNode(index++);
            value = ParseExpression();
         }
         
         if (_peek.Type == TokenType.Punctuation &&
             (_peek.String == "," || _peek.String == ";"))
         {
            GetNextToken();
         }
      }
      
      private TableExpressionNode ParseFieldList()
      {
         List<ExpressionNode> keys = new List<ExpressionNode>();
         List<ExpressionNode> values = new List<ExpressionNode>();
         int index = 1;

         while (!IsTokenMatch(_peek, TokenType.Punctuation, "}"))
         {
            ExpressionNode key;
            ExpressionNode value;
            ParseField(ref index, out key, out value);
            keys.Add(key);
            values.Add(value);
         }
         
         return new TableExpressionNode(keys.ToArray(), values.ToArray());
      }
      
      private string[] ParseParameterList()
      {
         ForceToken(TokenType.Punctuation, "(");
         string[] parameters;
         if (IsTokenMatch(_peek, TokenType.Punctuation, ")"))
         {
            parameters = new string[0];
         }
         else
         {
            parameters = ParseIdentifierList();
         }
         ForceToken(TokenType.Punctuation, ")");
         return parameters;
      }

      private string ParseIdentifier()
      {
         string name = _peek.String;
         ForceTokenType(TokenType.Identifier);
         return name;
      }

      private string[] ParseIdentifierList()
      {
         List<string> idList = new List<string>();
         idList.Add(ParseIdentifier());
         
         while (IsTokenMatch(_peek, TokenType.Punctuation, ","))
         {
            GetNextToken();
            idList.Add(ParseIdentifier());
         }
         
         return idList.ToArray();
      }

      private bool IsStatementBegin(Token token)
      {
         return token != Token.EOF &&
                token.String != "end" &&
                token.String != "until" &&
                token.String != "elseif" &&
                token.String != "else" &&
                token.String != "return" &&
                token.String != "break";
      }
      
      private StatementNode ParseStatement()
      {
         StatementNode stmt;
         
         if (IsTokenMatch(_peek, TokenType.Keyword, "do"))
         {
            stmt = new BlockStatementNode(ParseDoEndBlock());
         }
         else if (IsTokenMatch(_peek, TokenType.Keyword, "while"))
         {
            stmt = ParseWhile();
         }
         else if (IsTokenMatch(_peek, TokenType.Keyword, "repeat"))
         {
            stmt = ParseRepeat();
         }
         else if (IsTokenMatch(_peek, TokenType.Keyword, "if"))
         {
            stmt = ParseIf();
         }
         else if (IsTokenMatch(_peek, TokenType.Keyword, "for"))
         {
            stmt = ParseFor();
         }
         else if (IsTokenMatch(_peek, TokenType.Keyword, "function"))
         {
            stmt = ParseFunction();
         }
         else if (IsTokenMatch(_peek, TokenType.Keyword, "local"))
         {
            stmt = ParseLocal();
         }
         else
         {
            stmt = ParseAssignOrFunctionCall();
         }
         
         if (IsTokenMatch(_peek, TokenType.Punctuation, ";")) GetNextToken();
         
         return stmt;
      }

      private void ForceTokenType(TokenType type)
      {
         if (_peek.Type != type)
         {
            throw new Exception(string.Format("Invalid token (expected {0}, got {1})", type, _peek.Type));
         }

         GetNextToken();
      }
      
      private void ForceToken(TokenType type, string str)
      {
         if (!IsTokenMatch(_peek, type, str))
         {
            throw new Exception("Invalid token");
         }
         
         GetNextToken();
      }
      
      private bool IsTokenMatch(Token token, TokenType type, string str)
      {
         return token.Type == type && token.String == str;
      }
   }
}
